package top.v5it.japi.plus.data.jdbc.mybatis;

import cn.hutool.extra.spring.SpringUtil;
import com.github.pagehelper.PageHelper;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.SqlSession;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
 * 基类，提供默认方法
 *
 * @author zhanpu
 * @date 2021/8/18
 */
public interface MybatisMapper<T, ID> {

    /**
     * {@link SqlSession}
     *
     * @return SqlSession
     */
    default SqlSession getSqlSession() {
        return SpringUtil.getBean(SqlSession.class);
    }

    /**
     * 获取包路径
     *
     * @return 返回调用者包路径
     */
    default String getPackageName() {
        return this.getClass().getInterfaces()[0].getName();
    }

    /**
     * 获取statement
     *
     * @param id
     * @return
     */
    default String getStatement(String id) {
        return getPackageName() + "." + id;
    }

    /**
     * 插入数据方法
     *
     * @param entity 实体
     * @param <S>    实体类型
     * @return 返回受影响行数
     */
    default <S extends T> int insert(S entity) {
        return insert("insert", entity);
    }

    /**
     * 插入数据方法
     *
     * @param statement
     * @param parameter
     * @return
     */
    default int insert(String statement, Object parameter) {
        return getSqlSession().update(getStatement(statement), parameter);
    }

    /**
     * 更新数据方法
     *
     * @param entity 实体
     * @param <S>    实体类型
     * @return 返回受影响行数
     */
    default <S extends T> int update(S entity) {
        return update("update", entity);
    }

    /**
     * 按dto参数更新数据方法
     *
     * @param dto 参数实体
     * @param <D> 参数类型
     * @return 返回受影响行数
     */
    default <D extends AbstractMybatisDTO> int updateByParameterDto(D dto) {
        return update("updateByParameterDto", dto);
    }

    /**
     * 按map参数更新数据方法
     *
     * @param paramMap 参数
     * @return 返回受影响行数
     */
    default int updateByMapParameter(Map<String, ?> paramMap) {
        return update("updateByParameter", paramMap);
    }

    /**
     * 按map参数更新数据方法
     *
     * @param statement 方法，对应mapper.xml中select中的id
     * @param parameter 查询参数
     * @return 返回受影响行数
     */
    default int update(String statement, Object parameter) {
        return getSqlSession().update(getStatement(statement), parameter);
    }

    /**
     * 根据条件查询实体
     *
     * @param entity 实体条件参数
     * @param <S>    实体类型
     * @return 返回实体
     */
    default <S extends T> List<S> selectAllByCondition(S entity) {
        return selectForList("selectAllByCondition", entity);
    }

    /**
     * 根据条件查询实体
     *
     * @param paramMap map格式参数
     * @param <E>      返回值类型
     * @return 返回实体
     */
    default <E> List<E> selectAllByMapCondition(Map<String, ?> paramMap) {
        return selectForList("selectAllByMapCondition", paramMap);
    }

    /**
     * 根据条件查询实体
     *
     * @param dto 必须是继承{@link AbstractMybatisDTO}
     * @param <D> 查询参数类型
     * @param <E> 返回值类型
     * @return 返回实体
     */
    default <D extends AbstractMybatisDTO, E> List<E> selectAllByDtoCondition(D dto) {
        return selectForList("selectAllByDtoCondition", dto);
    }

    /**
     * 根据条件查询实体
     *
     * @param parameter 查询参数
     * @param <E>       返回值类型
     * @return 返回实体
     */
    default <E> List<E> selectForList(Object parameter) {
        return selectForList("selectForList", parameter);
    }

    /**
     * 根据条件查询实体
     *
     * @param statement 方法，对应mapper.xml中select中的id
     * @param parameter 查询参数
     * @param <E>       返回值类型
     * @return 返回实体
     */
    default <E> List<E> selectForList(String statement, Object parameter) {
        return getSqlSession().selectList(getStatement(statement), parameter);
    }

    /**
     * 分页查询
     *
     * @param entity   实体
     * @param pageable 分布信息{@link Pageable}
     * @param <S>      实体类型
     * @param <E>      返回值实体类型
     * @return 分页记录
     */
    default <S extends T, E> Page<E> selectAllByCondition(S entity, Pageable pageable) {
        return selectAll("selectAllInPageByCondition", entity, pageable);
    }

    /**
     * 分页查询
     *
     * @param paramMap map格式参数
     * @param <E>      返回值实体类型
     * @param pageable 分页信息
     * @return 分页记录
     */
    default <E> Page<E> selectAllByMapCondition(Map<String, ?> paramMap, Pageable pageable) {
        return selectAll("selectAllInPageByMapCondition", paramMap, pageable);
    }

    /**
     * 分页查询
     *
     * @param dto      必须是继承{@link AbstractMybatisDTO}
     * @param <D>      查询参数类型
     * @param <E>      返回值实体类型
     * @param pageable 分页信息
     * @return 分页记录
     */
    default <D extends AbstractMybatisDTO, E> Page<E> selectAllByDtoCondition(D dto, Pageable pageable) {
        return selectAll("selectAllInPageByDtoCondition", dto, pageable);
    }

    /**
     * 分页查询
     *
     * @param parameter 查询参数
     * @param <E>       实体类型
     * @param pageable  分页信息
     * @return 分页记录
     */
    default <E> Page<E> selectAll(Object parameter, Pageable pageable) {
        return selectAll("selectAllInPage", parameter, pageable);
    }

    /**
     * 分页查询
     *
     * @param statement 方法，对应mapper.xml中select中的id
     * @param parameter 查询参数
     * @param <E>       实体类型
     * @param pageable  分页信息
     * @return 分页记录
     */
    default <E> Page<E> selectAll(String statement, Object parameter, Pageable pageable) {

        // 获取排序规则
        String orderBy = pageable.getSort().get()
                .map(t -> t.getProperty().concat(" ").concat(t.getDirection().toString()))
                .collect(Collectors.joining(", "));

        // 设置分页
        com.github.pagehelper.Page<T> page = PageHelper.startPage(pageable.getPageNumber() + 1, pageable.getPageSize(), orderBy);

        // 执行查询
        List<E> list = selectForList(statement, parameter);

        // 封装分页结果
        return new PageImpl<>(StreamSupport.stream(list.spliterator(), false).collect(Collectors.toList()), pageable,
                page.getTotal());
    }

    /**
     * 分页总记录数
     *
     * @param entity 实体
     * @param <S>    实体类型
     * @return 总记录数
     */
    default <S extends T> Long count(S entity) {
        return selectOne("count", entity);
    }

    /**
     * 批量插入数据方法
     *
     * @param entities 实体
     * @param <S>      实体类型
     * @return 返回受影响行数
     */
    default <S extends T> int insertAllInBatch(Collection<S> entities) {
        return insert("insertAllInBatch", entities);
    }

    /**
     * 批量更新数据方法
     *
     * @param entities 实体
     * @param <S>      实体类型
     * @return 返回受影响行数
     */
    default <S extends T> int updateAllInBatch(Collection<S> entities) {
        return update("updateAllInBatch", entities);
    }

    /**
     * 批量删除数据方法
     *
     * @param entities 实体
     * @param <S>      实体类型
     * @return 返回受影响行数
     */
    default <S extends T> int deleteAllInBatch(Collection<S> entities) {
        return getSqlSession().delete(getStatement("deleteAllInBatch"), entities);
    }

    /**
     * 根据id查询实体
     *
     * @param id  实体id
     * @param <E> 返回值实体类型
     * @return 返回实体
     */
    default <E> E selectById(ID id) {
        return selectOne("selectById", id);
    }

    /**
     * 查询单个唯一实体
     *
     * @param entity 实体条件参数
     * @param <S>    实体类型
     * @param <E>    返回值实体类型
     * @return 返回实体
     */
    default <S extends T, E> E selectOneByCondition(S entity) {
        return selectOne("selectOneByCondition", entity);
    }

    /**
     * 查询单个唯一实体
     *
     * @param paramMap map格式参数
     * @param <E>      返回值实体类型
     * @return 返回实体
     */
    default <E> E selectOneByMapCondition(Map<String, ?> paramMap) {
        return selectOne("selectOneByMapCondition", paramMap);
    }

    /**
     * 查询单个唯一实体
     *
     * @param dto 必须是继承{@link AbstractMybatisDTO}
     * @param <D> 查询参数类型
     * @return 返回实体
     */
    default <D extends AbstractMybatisDTO, E> E selectOneByDtoCondition(D dto) {
        return selectOne("selectOneByDtoCondition", dto);
    }

    /**
     * 查询单个唯一实体
     *
     * @param parameter 查询参数
     * @param <E>       返回值类型
     * @return 返回实体
     */
    default <E> E selectOne(Object parameter) {
        return selectOne("selectOne", parameter);
    }

    /**
     * 查询单个唯一实体
     *
     * @param statement 方法，对应mapper.xml中select中的id
     * @param parameter 查询参数
     * @param <E>       返回值类型
     * @return 返回实体
     */
    default <E> E selectOne(String statement, Object parameter) {
        return getSqlSession().selectOne(getStatement(statement), parameter);
    }

    /**
     * 流式查询
     *
     * @param entity 查询参数
     * @param <S>    返回值类型
     * @return 返回实体
     */
    default <S extends T> Cursor<S> selectCursorByCondition(S entity) {
        return selectCursor("selectCursorByCondition", entity);
    }

    /**
     * 流式查询
     *
     * @param paramMap 查询参数
     * @param <E>      返回值类型
     * @return 返回实体
     */
    default <E> Cursor<E> selectCursorByMapCondition(Map<String, ?> paramMap) {
        return selectCursor("selectCursorByMapCondition", paramMap);
    }

    /**
     * 流式查询
     *
     * @param dto 必须是继承{@link AbstractMybatisDTO}
     * @param <D> 查询参数类型
     * @param <E> 返回值类型
     * @return 返回实体
     */
    default <D extends AbstractMybatisDTO, E> Cursor<E> selectCursorByDtoCondition(D dto) {
        return selectCursor("selectCursorByDtoCondition", dto);
    }

    /**
     * 流式查询
     *
     * @param parameter 查询参数
     * @param <E>       返回值类型
     * @return 返回实体
     */
    default <E> Cursor<E> selectCursor(Object parameter) {
        return selectCursor("selectCursor", parameter);
    }

    /**
     * 流式查询
     *
     * @param statement 方法，对应mapper.xml中select中的id
     * @param parameter 查询参数
     * @param <E>       返回值类型
     * @return 返回实体
     */
    default <E> Cursor<E> selectCursor(String statement, Object parameter) {
        return getSqlSession().selectCursor(getStatement(statement), parameter);
    }

    /**
     * 按mapKey查询相应字段
     *
     * @param statement
     * @param parameter
     * @param mapKey
     * @param <K>
     * @param <V>
     * @return
     */
    default <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
        return getSqlSession().selectMap(getStatement(statement), parameter, mapKey);
    }

    /**
     * 按mapKey查询相应字段
     *
     * @param entity
     * @param mapKey
     * @param <K>
     * @param <V>
     * @param <S>
     * @return
     */
    default <K, V, S> Map<K, V> selectMapByCondition(S entity, String mapKey) {
        return selectMap("selectMapByCondition", entity, mapKey);
    }

    /**
     * 按mapKey查询相应字段
     *
     * @param paramMap
     * @param mapKey
     * @param <K>
     * @param <V>
     * @return
     */
    default <K, V> Map<K, V> selectMapByMapCondition(Map<String, ?> paramMap, String mapKey) {
        return selectMap("selectMapByMapCondition", paramMap, mapKey);
    }

    /**
     * 按mapKey查询相应字段
     *
     * @param dto
     * @param mapKey
     * @param <K>
     * @param <V>
     * @param <D>
     * @return
     */
    default <K, V, D extends AbstractMybatisDTO> Map<K, V> selectMapByDtoCondition(D dto, String mapKey) {
        return selectMap("selectMapByDtoCondition", dto, mapKey);
    }

    /**
     * 查询返回{@link ResultHandler}交由用户自己处理结果
     *
     * @param statement
     * @param parameter
     * @param handler
     */
    default void select(String statement, Object parameter, ResultHandler<?> handler) {
        getSqlSession().select(getStatement(statement), parameter, handler);
    }

    /**
     * 查询返回{@link ResultHandler}交由用户自己处理结果
     *
     * @param entity
     * @param handler
     * @param <S>
     */
    default <S extends T> void selectAllByCondition(S entity, ResultHandler<?> handler) {
        select("selectAllByConditionForResultHandler", entity, handler);
    }

    /**
     * 查询返回{@link ResultHandler}交由用户自己处理结果
     *
     * @param paramMap
     * @param handler
     */
    default void selectAllByMapCondition(Map<String, ?> paramMap, ResultHandler<?> handler) {
        select("selectAllByMapConditionForResultHandler", paramMap, handler);
    }

    /**
     * 查询返回{@link ResultHandler}交由用户自己处理结果
     *
     * @param dto
     * @param handler
     * @param <D>
     */
    default <D extends AbstractMybatisDTO> void selectAllByDtoCondition(D dto, ResultHandler<?> handler) {
        select("selectAllByDtoConditionForResultHandler", dto, handler);
    }

}
